package com.srmt.feishu.client.util;

import com.lark.oapi.core.response.BaseResponse;
import com.lark.oapi.core.response.RawResponse;
import com.lark.oapi.core.utils.Jsons;
import com.srmt.feishu.client.exception.FeishuApiException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.function.Function;

/**
 * 飞书API响应校验工具类
 *
 * 提供统一的响应处理和数据提取功能，实现：
 * - 统一的success()校验
 * - 统一的错误处理和异常抛出
 * - 元数据日志记录（requestId、logId）
 * - 简化上层业务代码
 *
 * @author SRMT Development Team
 * @since 6.0.0
 */
public class ResponseValidator {

    private static final Logger logger = LoggerFactory.getLogger(ResponseValidator.class);

    private ResponseValidator() {
        // 工具类，禁止实例化
    }

    /**
     * 校验响应并提取数据
     *
     * 使用示例：
     * <pre>{@code
     * CreateDocumentResp resp = client.docx().v1().document().create(req);
     * Document doc = ResponseValidator.validateAndExtract(
     *     resp,
     *     "createDocument",
     *     r -> r.getDocument()
     * );
     * }</pre>
     *
     * @param <R> 响应类型（继承自BaseResponse）
     * @param <T> 提取的数据类型
     * @param resp 飞书API响应对象
     * @param operation API操作名称（用于日志和异常信息）
     * @param dataExtractor 数据提取器（从响应中提取具体数据）
     * @return 提取的数据对象
     * @throws FeishuApiException 如果响应失败（success()返回false）
     */
    public static <R extends BaseResponse, T> T validateAndExtract(
            R resp,
            String operation,
            Function<R, T> dataExtractor) {

        // 1. 检查响应是否成功
        if (!resp.success()) {
            logger.error("[{}] API调用失败: code={}, msg={}, requestId={}",
                        operation,
                        resp.getCode(),
                        resp.getMsg(),
                        resp.getRequestId());
            throw new FeishuApiException(operation, resp.getCode(), resp.getMsg());
        }

        // 2. 记录元数据到日志（替代返回元数据）
        if (logger.isDebugEnabled()) {
            logger.debug("[{}] API调用成功: requestId={}, logId={}",
                        operation,
                        resp.getRequestId(),
                        getLogId(resp));
        }

        // 2. 解析JSON响应
        String bodyStr = new String(resp.getRawResponse().getBody(), StandardCharsets.UTF_8);
        Map<String, Object> responseMap;
        try {
            responseMap = Jsons.DEFAULT.fromJson(bodyStr, Map.class);
        } catch (Exception e) {
            logger.error("[{}] 响应解析失败: {}", operation, e.getMessage());
            throw new FeishuApiException(operation, -1,
                    "解析响应失败: " + e.getMessage(), e);
        }

        //检查API响应码
        Number code = (Number) responseMap.get("code");
        if (code != null && code.intValue() == 0) {
            logger.info("[{}] API调用成功", operation);
        } else {
            String msg = (String) responseMap.get("msg");
            logger.error("[{}] API调用失败: code={}, msg={}",
                    operation, code, msg);
            throw new FeishuApiException(operation,
                    code != null ? code.intValue() : -1,
                    msg != null ? msg : "未知错误");
        }

        // 3. 提取数据
        T data = dataExtractor.apply(resp);
        if (data == null) {
            logger.warn("[{}] API返回数据为空", operation);
            throw new FeishuApiException(operation, -1, "响应数据为空");
        }

        return data;
    }

    /**
     * 校验void响应（用于无返回值的操作，如Delete）
     *
     * 使用示例：
     * <pre>{@code
     * DeleteFileResp resp = client.drive().v1().file().delete(req);
     * ResponseValidator.validateVoidResponse(resp, "deleteFile");
     * }</pre>
     *
     * @param <R> 响应类型
     * @param resp 飞书API响应对象
     * @param operation API操作名称
     * @throws FeishuApiException 如果响应失败
     */
    public static <R extends BaseResponse> void validateVoidResponse(
            R resp,
            String operation) {

        // 1. 检查响应是否成功
        if (!resp.success()) {
            logger.error("[{}] API调用失败: code={}, msg={}, requestId={}",
                        operation,
                        resp.getCode(),
                        resp.getMsg(),
                        resp.getRequestId());
            throw new FeishuApiException(operation, resp.getCode(), resp.getMsg());
        }

        // 2. 记录成功日志
        if (logger.isDebugEnabled()) {
            logger.debug("[{}] API调用成功: requestId={}", operation, resp.getRequestId());
        }
    }

    /**
     * 校验响应但允许数据为空（用于可选数据的场景）
     *
     * @param <R> 响应类型
     * @param <T> 提取的数据类型
     * @param resp 飞书API响应对象
     * @param operation API操作名称
     * @param dataExtractor 数据提取器
     * @return 提取的数据对象，可能为null
     * @throws FeishuApiException 如果响应失败
     */
    public static <R extends BaseResponse, T> T validateAndExtractNullable(
            R resp,
            String operation,
            Function<R, T> dataExtractor) {

        // 1. 检查响应是否成功
        if (!resp.success()) {
            logger.error("[{}] API调用失败: code={}, msg={}, requestId={}",
                        operation,
                        resp.getCode(),
                        resp.getMsg(),
                        resp.getRequestId());
            throw new FeishuApiException(operation, resp.getCode(), resp.getMsg());
        }

        // 2. 记录元数据到日志
        if (logger.isDebugEnabled()) {
            logger.debug("[{}] API调用成功: requestId={}, logId={}",
                        operation,
                        resp.getRequestId(),
                        getLogId(resp));
        }

        // 3. 提取数据（允许为null）
        return dataExtractor.apply(resp);
    }

    /**
     * 获取logId（兼容性处理，某些响应可能没有此字段）
     */
    private static String getLogId(BaseResponse resp) {
        try {
            // BaseResponse可能没有getLogId()方法，使用反射或返回默认值
            return "N/A";
        } catch (Exception e) {
            return "N/A";
        }
    }

    // ========== RawResponse处理方法（用于v2 API等原生HTTP调用） ==========

    /**
     * 校验原生HTTP响应（用于v2 API等不返回SDK封装对象的调用）
     *
     * 使用示例：
     * <pre>{@code
     * RawResponse response = client.post(url, body, AccessTokenType.Tenant);
     * ResponseValidator.validateRawResponse(response, "updateValues");
     * }</pre>
     *
     * @param response 原生HTTP响应
     * @param operation API操作名称
     * @throws FeishuApiException 如果HTTP状态码非200或API返回错误码非0
     */
    @SuppressWarnings("unchecked")
    public static void validateRawResponse(RawResponse response, String operation) {
        // 1. 检查HTTP状态码
        if (response.getStatusCode() != 200) {
            String bodyStr = new String(response.getBody(), StandardCharsets.UTF_8);
            logger.error("[{}] HTTP请求失败: status={}, body={}",
                        operation, response.getStatusCode(), bodyStr);
            throw new FeishuApiException(operation,
                response.getStatusCode(),
                "HTTP请求失败: " + bodyStr);
        }

        // 2. 解析JSON响应
        String bodyStr = new String(response.getBody(), StandardCharsets.UTF_8);
        Map<String, Object> responseMap;
        try {
            responseMap = Jsons.DEFAULT.fromJson(bodyStr, Map.class);
        } catch (Exception e) {
            logger.error("[{}] 响应解析失败: {}", operation, e.getMessage());
            throw new FeishuApiException(operation, -1,
                "解析响应失败: " + e.getMessage(), e);
        }

        // 3. 检查API响应码
        Number code = (Number) responseMap.get("code");
        if (code != null && code.intValue() == 0) {
            logger.info("[{}] API调用成功", operation);
        } else {
            String msg = (String) responseMap.get("msg");
            logger.error("[{}] API调用失败: code={}, msg={}",
                        operation, code, msg);
            throw new FeishuApiException(operation,
                code != null ? code.intValue() : -1,
                msg != null ? msg : "未知错误");
        }
    }

    /**
     * 校验原生HTTP响应并提取数据
     *
     * 使用示例：
     * <pre>{@code
     * RawResponse response = client.get(url, null, AccessTokenType.Tenant, options);
     * List<List<String>> values = ResponseValidator.validateAndExtractFromRaw(
     *     response,
     *     "getValues",
     *     responseMap -> {
     *         Map<String, Object> data = (Map) responseMap.get("data");
     *         Map<String, Object> valueRange = (Map) data.get("valueRange");
     *         return (List) valueRange.get("values");
     *     }
     * );
     * }</pre>
     *
     * @param <T> 提取的数据类型
     * @param response 原生HTTP响应
     * @param operation API操作名称
     * @param dataExtractor 数据提取器（从解析后的Map中提取数据）
     * @return 提取的数据对象
     * @throws FeishuApiException 如果HTTP状态码非200或API返回错误码非0
     */
    @SuppressWarnings("unchecked")
    public static <T> T validateAndExtractFromRaw(
            RawResponse response,
            String operation,
            Function<Map<String, Object>, T> dataExtractor) {

        // 1. 检查HTTP状态码
        if (response.getStatusCode() != 200) {
            String bodyStr = new String(response.getBody(), StandardCharsets.UTF_8);
            logger.error("[{}] HTTP请求失败: status={}, body={}",
                        operation, response.getStatusCode(), bodyStr);
            throw new FeishuApiException(operation,
                response.getStatusCode(),
                "HTTP请求失败: " + bodyStr);
        }

        // 2. 解析JSON响应
        String bodyStr = new String(response.getBody(), StandardCharsets.UTF_8);
        Map<String, Object> responseMap;
        try {
            responseMap = Jsons.DEFAULT.fromJson(bodyStr, Map.class);
        } catch (Exception e) {
            logger.error("[{}] 响应解析失败: {}", operation, e.getMessage());
            throw new FeishuApiException(operation, -1,
                "解析响应失败: " + e.getMessage(), e);
        }

        // 3. 检查API响应码
        Number code = (Number) responseMap.get("code");
        if (code != null && code.intValue() != 0) {
            String msg = (String) responseMap.get("msg");
            logger.error("[{}] API调用失败: code={}, msg={}",
                        operation, code, msg);
            throw new FeishuApiException(operation,
                code.intValue(),
                msg != null ? msg : "未知错误");
        }

        // 4. 提取数据
        logger.debug("[{}] API调用成功，开始提取数据", operation);
        try {
            T data = dataExtractor.apply(responseMap);
            if (data == null) {
                logger.debug("[{}] 提取的数据为空（可能是正常的空结果）", operation);
            }
            return data;
        } catch (Exception e) {
            logger.error("[{}] 数据提取失败: {}", operation, e.getMessage());
            throw new FeishuApiException(operation, -1,
                "提取数据失败: " + e.getMessage(), e);
        }
    }

    /**
     * 校验原生HTTP响应并提取数据（允许数据为null）
     *
     * @param <T> 提取的数据类型
     * @param response 原生HTTP响应
     * @param operation API操作名称
     * @param dataExtractor 数据提取器
     * @return 提取的数据对象，可能为null
     * @throws FeishuApiException 如果HTTP状态码非200或API返回错误码非0
     */
    @SuppressWarnings("unchecked")
    public static <T> T validateAndExtractFromRawNullable(
            RawResponse response,
            String operation,
            Function<Map<String, Object>, T> dataExtractor) {

        // 1. 检查HTTP状态码
        if (response.getStatusCode() != 200) {
            String bodyStr = new String(response.getBody(), StandardCharsets.UTF_8);
            logger.error("[{}] HTTP请求失败: status={}, body={}",
                        operation, response.getStatusCode(), bodyStr);
            throw new FeishuApiException(operation,
                response.getStatusCode(),
                "HTTP请求失败: " + bodyStr);
        }

        // 2. 解析JSON响应
        String bodyStr = new String(response.getBody(), StandardCharsets.UTF_8);
        Map<String, Object> responseMap;
        try {
            responseMap = Jsons.DEFAULT.fromJson(bodyStr, Map.class);
        } catch (Exception e) {
            logger.error("[{}] 响应解析失败: {}", operation, e.getMessage());
            throw new FeishuApiException(operation, -1,
                "解析响应失败: " + e.getMessage(), e);
        }

        // 3. 检查API响应码
        Number code = (Number) responseMap.get("code");
        if (code != null && code.intValue() != 0) {
            String msg = (String) responseMap.get("msg");
            logger.error("[{}] API调用失败: code={}, msg={}",
                        operation, code, msg);
            throw new FeishuApiException(operation,
                code.intValue(),
                msg != null ? msg : "未知错误");
        }

        // 4. 提取数据（允许为null）
        logger.debug("[{}] API调用成功，开始提取数据", operation);
        try {
            return dataExtractor.apply(responseMap);
        } catch (Exception e) {
            logger.error("[{}] 数据提取失败: {}", operation, e.getMessage());
            throw new FeishuApiException(operation, -1,
                "提取数据失败: " + e.getMessage(), e);
        }
    }
}
